﻿using System.Collections.Generic;
using System.IO;

namespace BrettM.Web.Http
{
    /// <summary>
    /// The class defines an ETag as specified by RFC 2616
    /// </summary>
    public class ETag
    {

        public ETag()
        {
            Value = string.Empty;
            IsWeak = false;
        }

        public ETag(string value)
            : this()
        {
            Value = value;
        }


        public ETag(string value, bool isWeak)
            : this(value)
        {
            IsWeak = isWeak;
        }

        public string Value { get; set; }
        public bool IsWeak { get; set; }

        private bool IsWildCard { get { return (Value == "*"); } }

        public static explicit operator string(ETag tag)
        {
            return tag.ToString();
        }

        public static bool operator ==(ETag lhs, string rhs)
        {
            if (rhs.Length > 0 && lhs.IsWildCard) return true;
            return (string.Compare(rhs, lhs.Value, lhs.IsWeak) == 0);
        }

        public static bool operator !=(ETag lhs, string rhs)
        {
            return !(lhs == rhs);
        }

        public static bool operator ==(string lhs, ETag rhs)
        {
            if (lhs.Length > 0 && rhs.IsWildCard) return true;
            return (string.Compare(lhs, rhs.Value, rhs.IsWeak) == 0);
        }

        public static bool operator !=(string lhs, ETag rhs)
        {
            return !(lhs == rhs);
        }

        public static ETagList ParseETags(string rawString)
        {
            var cols = new ETagList();

            if (rawString == null) return cols;

            var isWeak = false;
            var inQuote = false;
            var quoteEnded = false;
            var word = string.Empty;

            for (var i = 0; i < rawString.Length; i++)
            {
                var ch = rawString[i];

                switch (ch)
                {
                    case ' ':
                        if (inQuote) goto default;
                        break;

                    case '"':
                        {
                            bool isDoubleQuote = ((i + 1) < rawString.Length && rawString[i + 1] == '"');
                            if (inQuote)
                            {
                                if (isDoubleQuote)
                                {
                                    word += "\"";
                                    i++;
                                    break;
                                }
                                inQuote = false;
                                quoteEnded = true;
                                break;
                            }
                            if (isDoubleQuote)
                            {
                                word += "\"";
                                i++;
                                if (((i + 1) < rawString.Length && rawString[i + 1] == '"'))
                                {
                                    i++;
                                    inQuote = true;
                                }
                                break;
                            }
                            if (word.Length > 0) throw new InvalidDataException((i + 1).ToString());
                            inQuote = true;
                        }
                        break;
                    case 'W':
                        if (inQuote) goto default;
                        if (word.Length > 0) throw new InvalidDataException((i + 1).ToString());
                        if (((i + 1) < rawString.Length && rawString[i + 1] == '/'))
                        {
                            isWeak = true;
                            i++;
                            break;
                        }
                        throw new InvalidDataException((i + 1).ToString());
                    case '*':
                        if (inQuote || quoteEnded) throw new InvalidDataException((i + 1).ToString());
                        word = "*";
                        quoteEnded = true;
                        break;

                    case ',':
                        if (inQuote) goto default;

                        cols.Add(new ETag(word, isWeak));
                        word = string.Empty;
                        quoteEnded = false;
                        isWeak = false;
                        break;


                    default:
                        if (!inQuote || quoteEnded)
                            throw new InvalidDataException((i + 1).ToString());
                        word += ch;
                        break;
                }
            }
            if (inQuote) throw new InvalidDataException((rawString.Length).ToString());
            if (word.Trim() != string.Empty)
                cols.Add(new ETag(word, isWeak));


            return cols;
        }

        public override string ToString()
        {
            if (Value == null) return null;
            return string.Format(IsWeak ? "W/\"{0}\"" : "\"{0}\"", Value.Replace("\"", "\"\""));
        }
    }
}
